#define TAG "NativeMP3Decoder"   
  
  
#include "FileSystem.h"   
  
#include "Mad.h"   
#include "NativeMP3Decoder.h"   
#include <stdio.h>   
#include <string.h>   
#include <stdlib.h>   
#include <android/log.h>   
  
  
  
#define SHRT_MAX (32767)   
#define INPUT_BUFFER_SIZE   (8192/4)   
#define OUTPUT_BUFFER_SIZE  8192 /* Must be an integer multiple of 4. */   
  
//int g_size;   
  
extern int file_open(const char *filename, int flags);  
extern int64_t file_read(T_pFILE fd, unsigned char *buf, int64_t size);  
extern int64_t file_write(T_pFILE fd, unsigned char *buf, int64_t size);  
extern int64_t file_seek(T_pFILE fd, int64_t pos, int whence);  
extern int file_close(T_pFILE fd);  
  
/** 
 * Struct holding the pointer to a wave file. 
 */  
typedef struct  
{  
    int64_t size;  
    int64_t fileStartPos;  
    T_pFILE file;  
    struct mad_stream stream;  
    struct mad_frame frame;  
    struct mad_synth synth;  
    mad_timer_t timer;  
    int leftSamples;  
    int64_t offset;  
    unsigned char keyBuffer[256];  
    unsigned char inputBuffer[INPUT_BUFFER_SIZE];  
} MP3FileHandle;  
  
  
/** static WaveFileHandle array **/  
static inline int readNextFrame( MP3FileHandle* mp3 );  
  
static MP3FileHandle* Handle = NULL;  
unsigned int g_Samplerate;  
  
/** 
 * Seeks a free handle in the handles array and returns its index or -1 if no handle could be found 
 */  
  
static inline void closeHandle()  
{  
    file_close( Handle->file);  
    mad_synth_finish(&Handle->synth);  
    mad_frame_finish(&Handle->frame);  
    mad_stream_finish(&Handle->stream);  
    free(Handle);  
    Handle = NULL;  
}  
  
static inline signed short fixedToShort(mad_fixed_t Fixed)  
{  
    if(Fixed>=MAD_F_ONE)  
        return(SHRT_MAX);  
    if(Fixed<=-MAD_F_ONE)  
        return(-SHRT_MAX);  
  
    Fixed=Fixed>>(MAD_F_FRACBITS-15);  
    return((signed short)Fixed);  
}  
  
  
int64_t  NativeMP3Decoder_init(char * filepath,int64_t start,int64_t size)  
{  
  
    //__android_log_print(ANDROID_LOG_INFO, TAG, "start = %d, size = %d *******\n",start,size);  
    
    //g_size=size;   
  	int64_t filesize;
  
    T_pFILE fileHandle = file_open( filepath, _FMODE_READ);  
    if( fileHandle == 0 )  
        return -1;  
  
    MP3FileHandle* mp3Handle = (MP3FileHandle*)malloc(sizeof(MP3FileHandle));  
    memset(mp3Handle, 0, sizeof(MP3FileHandle));  
    mp3Handle->file = fileHandle;  
  
  
    mp3Handle->size = size;   
    mp3Handle->fileStartPos= start;  
  	
  	filesize = file_seek( mp3Handle->file,0, SEEK_END); 
  	file_seek( mp3Handle->file,start, SEEK_SET);  
  	file_read( mp3Handle->file,mp3Handle->keyBuffer,256);
  	/*__android_log_print(ANDROID_LOG_INFO, TAG, "%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,\n",
  	mp3Handle->keyBuffer[0],mp3Handle->keyBuffer[1],mp3Handle->keyBuffer[2],mp3Handle->keyBuffer[3],mp3Handle->keyBuffer[4],
  	mp3Handle->keyBuffer[251],mp3Handle->keyBuffer[252],mp3Handle->keyBuffer[253],mp3Handle->keyBuffer[254],mp3Handle->keyBuffer[255]);  
    */
    file_seek( mp3Handle->file, start + 256, SEEK_SET);  
  
    mad_stream_init(&mp3Handle->stream);  
    mad_frame_init(&mp3Handle->frame);  
    mad_synth_init(&mp3Handle->synth);  
    mad_timer_reset(&mp3Handle->timer);  
  
    Handle = mp3Handle;  
  
  
    readNextFrame( Handle );  
  
    g_Samplerate = Handle->frame.header.samplerate;  
    return filesize;  
}  
 
int64_t  NativeMP3Decoder_seek(int64_t seekpos,int64_t len)  
{  
  
    
    if(Handle == NULL)  
	{
		__android_log_print(ANDROID_LOG_INFO, TAG, "not open mp3 file");  
        return -1;  
	}

    Handle->size = len;   
    Handle->fileStartPos= seekpos;  
  
    file_seek( Handle->file, seekpos, SEEK_SET);  
  
    mad_stream_init(&Handle->stream);  
    mad_frame_init(&Handle->frame);  
    mad_synth_init(&Handle->synth);  
    mad_timer_reset(&Handle->timer);  
  
    readNextFrame( Handle );  
  
    g_Samplerate = Handle->frame.header.samplerate;  
    
   // __android_log_print(ANDROID_LOG_INFO, TAG, "seekposs = %lld size = %lld\n",seekpos,Handle->size);
    return Handle->frame.header.bitrate;  
}    
static void DecrypBuff(unsigned char *key,unsigned char * orgbuff,int size)
{
	int i;
	for(i = 0; i < size;i++)
	{
		orgbuff[i] = key[orgbuff[i]];
	}
}
static inline int readNextFrame( MP3FileHandle* mp3 )  
{  
	int64_t readBytes,filepos=0,leftOver = 0,i;
    do  
    {  
        if( mp3->stream.buffer == 0 || mp3->stream.error == MAD_ERROR_BUFLEN )  
        {  
            int inputBufferSize = 0;  
  
            if( mp3->stream.next_frame != 0 )  
            {    
                leftOver = mp3->stream.bufend - mp3->stream.next_frame;  
                for(  i= 0; i < leftOver; i++ )  
                    mp3->inputBuffer[i] = mp3->stream.next_frame[i];  
				filepos = file_seek( mp3->file, 0, SEEK_CUR);  
				if(filepos >=  mp3->fileStartPos + mp3->size)
				{
					//__android_log_print(ANDROID_LOG_INFO, TAG, "seekpos=%d >= start=%d + size=%d\n",filepos,mp3->fileStartPos,mp3->size);
					return 0;
				}
				if(INPUT_BUFFER_SIZE - leftOver + filepos >  mp3->fileStartPos + mp3->size)
					 readBytes = file_read(mp3->file,mp3->inputBuffer + leftOver, mp3->fileStartPos + mp3->size - filepos);
				else
					 readBytes = file_read( mp3->file, mp3->inputBuffer + leftOver, INPUT_BUFFER_SIZE - leftOver);                      
                if( readBytes == 0 )  
				{
					//__android_log_print(ANDROID_LOG_INFO, TAG, "end file\n");
					return 0;
				}
                inputBufferSize = leftOver + readBytes;  
            }  
            else  
            {  
                  
                int readBytes = file_read( mp3->file, mp3->inputBuffer, INPUT_BUFFER_SIZE);  
                if( readBytes == 0 )  
 				{
					//__android_log_print(ANDROID_LOG_INFO, TAG, "end file \n");
					return 0;
				}
                inputBufferSize = readBytes;  
            }  
  			DecrypBuff(mp3->keyBuffer,mp3->inputBuffer + leftOver, inputBufferSize - leftOver);
            mad_stream_buffer( &mp3->stream, mp3->inputBuffer, inputBufferSize );  
            mp3->stream.error = MAD_ERROR_NONE;  
           // __android_log_print(ANDROID_LOG_INFO, TAG, "seekpos=%lld >= start=%lld + size=%lld\n",filepos,mp3->fileStartPos,mp3->size);
        }  
        if( mad_frame_decode( &mp3->frame, &mp3->stream ) )  
        {  
  
            if( mp3->stream.error == MAD_ERROR_BUFLEN ||(MAD_RECOVERABLE(mp3->stream.error)))  
                continue;  
            else  
                return 0;  
        }  
        else  
            break;  
    }  
    while( 1 );  
  
    mad_timer_add( &mp3->timer, mp3->frame.header.duration );  
    mad_synth_frame( &mp3->synth, &mp3->frame );  
    mp3->leftSamples = mp3->synth.pcm.length;  
    mp3->offset = 0;  
  
    return -1;  
}  
  
  
  
int64_t NativeMP3Decoder_readSamples(short *target, int64_t size)  
{  
  
    MP3FileHandle* mp3 = Handle;  
    //short* target = (short*)env->GetDirectBufferAddress(buffer);   
    int64_t pos=0;  
    int64_t idx = 0;  
    while( idx != size )  
    {  
        if( mp3->leftSamples > 0 )  
        {  
            for( ; idx < size && mp3->offset < mp3->synth.pcm.length; mp3->leftSamples--, mp3->offset++ )  
            {  
                int value = fixedToShort(mp3->synth.pcm.samples[0][mp3->offset]);  
  
                if( MAD_NCHANNELS(&mp3->frame.header) == 2 )  
                {  
                    value += fixedToShort(mp3->synth.pcm.samples[1][mp3->offset]);  
                    value /= 2;  
                }  
  
                target[idx++] = value;  
            }  
        }  
        else  
        {  
              
            pos = file_seek( mp3->file, 0, SEEK_CUR);  
            int result = readNextFrame( mp3);  
            if( result == 0 )  
            {
            	//__android_log_print(ANDROID_LOG_INFO, TAG, "*****result = 0**\n"); 
                return idx;  
             }
        }  
        //__android_log_print(ANDROID_LOG_INFO, TAG, "*result = %d,idx=%lld**leftsamples=%d\n",result,idx,mp3->leftSamples);
    }  
/*    if(pos > mp3->fileStartPos + mp3->size) 
    { 
         
        __android_log_print(ANDROID_LOG_INFO, TAG, "idx = %d,pos = %d, start = %d,size = %d\n",idx,pos,mp3->fileStartPos,mp3->size);
        return idx; 
    } 
  
    if( idx > size )  
    {
    	__android_log_print(ANDROID_LOG_INFO, TAG, "*****idx > size**\n"); 
    	return 0;  
    }*/ 
    //__android_log_print(ANDROID_LOG_INFO, TAG, "idx = %d,pos = %d, start = %d,size = %d\n",idx,pos,mp3->fileStartPos,mp3->size); 
    return idx;//pos;  
  
}  
  
int64_t NativeMP3Decoder_getAduioSamplerate()  
{  
    return g_Samplerate;  
  
}  
  
  
void  NativeMP3Decoder_closeAduioFile()  
{  
    if( Handle != 0 )  
    {  
        closeHandle();  
        Handle = 0;  
    }  
}  
